XT Web API & ASP.NET Core

Clear topic-by-topic flow for hands-on understanding

1. Web API 2. REST 3. Security 4. Host 5. Kestrel 6. Middleware

Topic 1: Designing & Building a Web API

Concept: A Web API lets applications communicate over HTTP. ASP.NET Core builds RESTful APIs using controllers.

Step 1: Create the project

Terminal
dotnet new webapi -n MyFirstApi
cd MyFirstApi
dotnet run

Step 2: Program.cs configuration

Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();
Key takeaway: AddControllers() registers controller support. MapControllers() maps incoming HTTP requests to controller actions.

Topic 2: Designing RESTful Interface

Concept: REST uses HTTP verbs: GET, POST, PUT, DELETE. Routing maps URLs to controllers and actions.

Hands-on: REST controller simulation

This console simulation shows how a RESTful Products API behaves. You can paste it into .NET Fiddle.

Console Simulation
using System;
using System.Collections.Generic;
using System.Linq;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class ProductsController
{
    private static List<Product> _db = new List<Product>
    {
        new Product { Id = 1, Name = "Laptop", Price = 1200 },
        new Product { Id = 2, Name = "Mouse", Price = 25 }
    };

    // GET: api/products
    public void Get()
    {
        Console.WriteLine("HTTP 200 OK:");
        foreach (var p in _db)
            Console.WriteLine($"  [{p.Id}] {p.Name} - ${p.Price}");
    }

    // POST: api/products
    public void Post(string name, decimal price)
    {
        var newProduct = new Product
        {
            Id = _db.Max(p => p.Id) + 1,
            Name = name,
            Price = price
        };

        _db.Add(newProduct);
        Console.WriteLine($"HTTP 201 Created: Added '{newProduct.Name}' (ID: {newProduct.Id})");
    }

    // DELETE: api/products/1
    public void Delete(int id)
    {
        var product = _db.FirstOrDefault(p => p.Id == id);

        if (product != null)
        {
            _db.Remove(product);
            Console.WriteLine($"HTTP 204 No Content: Deleted ID {id}");
        }
        else
        {
            Console.WriteLine($"HTTP 404 Not Found: ID {id} does not exist.");
        }
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("--- Starting Simulated RESTful API ---\n");
        var api = new ProductsController();

        api.Get();
        Console.WriteLine();

        api.Post("Keyboard", 75);
        Console.WriteLine();

        api.Get();
        Console.WriteLine();

        api.Delete(1);
        Console.WriteLine();

        api.Delete(99);
    }
}
Key takeaway: GET returns data, POST creates, DELETE removes. Status codes matter: 200, 201, 204, 404.

Topic 3: Securing a Web API

Concept: JWT is commonly used. The client authenticates, receives a token, and sends it in the Authorization header.

Generate a JWT token

JWT Basics
using System;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;

public class Program
{
    public static void Main()
    {
        var secretKey = "ThisIsMySuperSecretKeyForJwtToken123!";
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, "user123"),
            new Claim(JwtRegisteredClaimNames.Email, "user@example.com"),
            new Claim(ClaimTypes.Role, "Admin")
        };

        var token = new JwtSecurityToken(
            issuer: "MyApp",
            audience: "MyAppUsers",
            claims: claims,
            expires: DateTime.Now.AddMinutes(30),
            signingCredentials: credentials
        );

        var tokenHandler = new JwtSecurityTokenHandler();
        var jwtString = tokenHandler.WriteToken(token);

        Console.WriteLine("--- Generated JWT Token ---");
        Console.WriteLine(jwtString);
        Console.WriteLine("\nClient sends it as: Authorization: Bearer <token>");
    }
}

ASP.NET Core JWT setup

Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "MyApp",
            ValidAudience = "MyAppUsers",
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes("ThisIsMySuperSecretKeyForJwtToken123!"))
        };
    });

app.UseAuthentication();
app.UseAuthorization();
Key takeaway: UseAuthentication() identifies the user. UseAuthorization() checks permissions. Authentication must come before Authorization.

Topic 4: ASP.NET Core Runtime Environment & Host

Concept: The Generic Host manages startup, dependency injection, logging, configuration, and lifetime.

Host lifecycle simulation

Console Simulation
using System;

public class WebApplication
{
    public static WebApplicationBuilder CreateBuilder(string[] args)
    {
        Console.WriteLine("[Host] Creating Builder...");
        return new WebApplicationBuilder();
    }

    public static void Run(string message)
    {
        Console.WriteLine("[Host] Application Started.");
        Console.WriteLine($"[Host] {message}");
        Console.WriteLine("[Host] Listening for requests...");
    }
}

public class WebApplicationBuilder
{
    public void AddServices()
    {
        Console.WriteLine("[Host] Configuring Dependency Injection...");
        Console.WriteLine("  - Registered: ILogger, IConfiguration, Controllers");
    }

    public void Build()
    {
        Console.WriteLine("[Host] Building the application pipeline...");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.AddServices();
        builder.Build();

        WebApplication.Run("Host is running on http://localhost:5000");
    }
}
Key takeaway: Flow is: CreateBuilder → AddServices → Build → Run. The host starts Kestrel and manages the app lifetime.

Topic 5: Embedded HTTP Server (Kestrel)

Concept: Kestrel is the default cross-platform embedded web server in ASP.NET Core. It is fast, lightweight, and part of the app process.

Configure Kestrel in code

Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024 * 1024;

    serverOptions.ListenAnyIP(5000);
});

var app = builder.Build();

app.MapGet("/", () => "Hello from Kestrel Embedded Server!");
app.Run();

Configure Kestrel in appsettings.json

appsettings.json
{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "Https": {
        "Url": "https://localhost:5001"
      }
    }
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  },
  "AllowedHosts": "*"
}
Key takeaway: Kestrel is the default server. You can configure ports, HTTPS, and limits in code or appsettings.json.

Topic 6: ASP.NET Core Middleware

Concept: Middleware components form a request pipeline. Each can process the request, pass it on, or stop it.

Middleware pipeline simulation

Console Simulation
using System;
using System.Threading.Tasks;

public class HttpContext
{
    public string RequestPath { get; set; } = "/api/data";
}

public delegate Task RequestDelegate(HttpContext context);

public class Program
{
    public static async Task Main()
    {
        Console.WriteLine("--- Starting Middleware Pipeline ---\n");

        RequestDelegate middleware1 = async (context) =>
        {
            Console.WriteLine("[Middleware 1] Request started: " + context.RequestPath);
            await Task.CompletedTask;
        };

        RequestDelegate middleware2 = async (context) =>
        {
            Console.WriteLine("[Middleware 2] Authenticating...");
            Console.WriteLine("[Middleware 2] User Authenticated!");
            await Task.CompletedTask;
        };

        RequestDelegate endpoint = async (context) =>
        {
            Console.WriteLine("[Endpoint] Processing request...");
            Console.WriteLine("[Endpoint] Returning HTTP 200 OK");
            await Task.CompletedTask;
        };

        RequestDelegate pipeline = async (context) =>
        {
            await middleware1(context);
            await middleware2(context);
            await endpoint(context);
        };

        var httpContext = new HttpContext();
        await pipeline(httpContext);

        Console.WriteLine("\n--- Pipeline Finished ---");
    }
}

Real ASP.NET Core middleware order

Program.cs
var app = builder.Build();

// ORDER IS CRITICAL
app.UseExceptionHandler("/Error");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();
Key takeaway: Middleware order matters. Authentication must come before Authorization. Routing must come before controllers.